Imports Aricie.ComponentModel
Imports System.IO
Imports Aricie.Services
Imports System.Xml
Imports System.IO.Compression
Imports System.ComponentModel
Imports System.Globalization
Imports Newtonsoft.Json

Namespace Aricie.DNN.Caching

    <Serializable()> _
    Public Class WrappedDistributedCacheObject
        Inherits WrappedCacheObject

        Private _DistributionType As CacheDistributionType
        <NonSerialized()> _
        Private _CompressionThresold As Integer

        Private _IsCompressed As Boolean
        Private _SerializedValue As Byte()

        Private _SerializedTypeName As String = ""

        Private _CallBackDelegate As DelegateInfo(Of CacheItemRemovedCallback)


        Public Sub New()

        End Sub

        Public Sub New(ByVal value As Object, ByVal job As CacheJob)
            MyBase.New(value, job)
            Me._DistributionType = job.Strategy.DistributionType
            Me._CompressionThresold = job.Strategy.CompressionThresold
        End Sub


        Public Sub New(ByVal job As CacheJob, ByVal wrappedObject As WrappedCacheObject)
            MyBase.New(wrappedObject.Value, wrappedObject.PersistAppRestart, Nothing, wrappedObject.AbsoluteExpiration, wrappedObject.SlidingExpriration, wrappedObject.Priority, wrappedObject.ItemRemovedCallBack)
            Me.CreatedDate = wrappedObject.CreatedDate
            Me._DistributionType = job.Strategy.DistributionType
            Me._CompressionThresold = job.Strategy.CompressionThresold
            If Not job.Strategy.DisableReflectedCallBacks AndAlso Me._ItemRemovedCallBack IsNot Nothing Then
                Me._CallBackDelegate = New DelegateInfo(Of CacheItemRemovedCallback)(Me._ItemRemovedCallBack)
            End If

            If job.Strategy.TimeOutMode = TimeOutMode.Constant Then
                If job.Strategy.DisableSlidingEmulation Then
                    Me._AbsoluteExpiration = Now.Add(TimeSpan.FromSeconds(job.Strategy.TimeOutConstant))
                Else
                    Me._SlidingExpriration = TimeSpan.FromSeconds(job.Strategy.TimeOutConstant)
                End If
            End If
        End Sub



#Region "Public properties"

        Public Property DistributionType() As CacheDistributionType
            Get
                Return _DistributionType
            End Get
            Set(ByVal value As CacheDistributionType)
                _DistributionType = value
            End Set
        End Property

        Public Property SerializedTypeName() As String
            Get
                Return _SerializedTypeName
            End Get
            Set(ByVal value As String)
                _SerializedTypeName = value
            End Set
        End Property

        Public Property SerializedValue() As Byte()
            Get
                Return _SerializedValue
            End Get
            Set(ByVal value As Byte())
                _SerializedValue = value
            End Set
        End Property

        Public Property CallBackDelegate() As DelegateInfo(Of CacheItemRemovedCallback)
            Get
                Return _CallBackDelegate
            End Get
            Set(ByVal value As DelegateInfo(Of CacheItemRemovedCallback))
                _CallBackDelegate = value
            End Set
        End Property

        Public Property IsCompressed() As Boolean
            Get
                Return _IsCompressed
            End Get
            Set(ByVal value As Boolean)
                _IsCompressed = value
            End Set
        End Property


#End Region

#Region "Public methods"

        Public Sub ProcessSerialization()
            If Me._SerializedValue Is Nothing Then
                Select Case Me._DistributionType
                    Case CacheDistributionType.NotDistributed, CacheDistributionType.Serialized, CacheDistributionType.Undefined
                        Exit Sub
                        'Case CacheDistributionType.Serialized
                        '    Dim vType As Type = Me._Value.GetType
                        '    If vType.IsGenericType Then
                        '        Dim args As Type() = vType.GetGenericArguments
                        '        For Each argT As Type In args
                        '            If argT Is vType Then
                        '                Throw New ApplicationException(String.Format("Generic type {0} with self referencing argument will break with a stack overflow with default WCF serialization", ReflectionHelper.GetSafeTypeName(vType)))
                        '            End If
                        '        Next
                        '    End If
                        '    Exit Sub
                    Case CacheDistributionType.XmlSerialized, CacheDistributionType.NativeBinary, CacheDistributionType.JsonNet
                        Using memStream As New MemoryStream
                            Select Case Me._DistributionType
                                Case CacheDistributionType.XmlSerialized, CacheDistributionType.JsonNet
                                    Me._SerializedTypeName = ReflectionHelper.GetSafeTypeName(_Value.GetType)
                                    Select Case Me._DistributionType
                                        Case CacheDistributionType.XmlSerialized
                                            Using xw As New System.Xml.XmlTextWriter(memStream, Encoding.UTF8)
                                                Aricie.Services.ReflectionHelper.Serialize(Me._Value, DirectCast(xw, XmlWriter))
                                            End Using
                                        Case CacheDistributionType.JsonNet
                                            Using sw As New StreamWriter(memStream, Encoding.UTF8)
                                                Dim jsonSettings As New JsonSerializerSettings()
                                                Using jw As New JsonTextWriter(sw)
                                                    Dim serializer As JsonSerializer = JsonSerializer.Create(jsonSettings)
                                                    serializer.Serialize(jw, Value)
                                                End Using
                                            End Using
                                    End Select
                                Case CacheDistributionType.NativeBinary
                                    ReflectionHelper.Instance.BinaryFormatter.Serialize(memStream, Me._Value)

                            End Select
                           
                            Me._SerializedValue = memStream.ToArray
                        End Using
                    Case CacheDistributionType.AsString
                        Me._SerializedTypeName = ReflectionHelper.GetSafeTypeName(_Value.GetType)
                        Me._SerializedValue = UTF8Encoding.UTF8.GetBytes(DirectCast(Me._Value, IConvertible).ToString(CultureInfo.InvariantCulture))
                End Select
                If DistributedCachingProviderBase.Settings.CloudSettings.EnableCompression AndAlso Me._SerializedValue.Length > Me._CompressionThresold * 1024 Then
                    Me._SerializedValue = Compress(Me._SerializedValue)
                    Me._IsCompressed = True
                End If
                Me._Value = Nothing
            End If
        End Sub

#End Region


#Region "Private methods"


        Public Overrides Sub GetCallBackDelegate()
            If Me._CallBackDelegate IsNot Nothing Then
                Try
                    Me._ItemRemovedCallBack = DirectCast(Me.CallBackDelegate.GetDelegate(), CacheItemRemovedCallback)
                Catch ex As Exception
                    DotNetNuke.Services.Exceptions.Exceptions.LogException(ex)
                End Try
            End If
        End Sub

        Public Overrides Sub GetValue()
            If Me._SerializedValue IsNot Nothing Then
                If Me._IsCompressed Then
                    Me._SerializedValue = Decompress(Me._SerializedValue)
                    'Me._IsCompressed = False
                End If
                Select Case Me._DistributionType
                    Case CacheDistributionType.XmlSerialized, CacheDistributionType.NativeBinary, CacheDistributionType.JsonNet

                        Try
                            Using memStream As New MemoryStream(Me._SerializedValue)
                                Select Case Me._DistributionType
                                    Case CacheDistributionType.XmlSerialized, CacheDistributionType.JsonNet
                                        If Me._SerializedTypeName <> "" Then
                                            Dim objType As Type = ReflectionHelper.CreateType(Me._SerializedTypeName)
                                            Select Case Me._DistributionType
                                                Case CacheDistributionType.XmlSerialized
                                                    Using reader As New XmlTextReader(memStream)
                                                        Me._Value = Aricie.Services.ReflectionHelper.Deserialize(objType, reader)
                                                    End Using
                                                Case CacheDistributionType.JsonNet
                                                    Dim jsonSettings As New JsonSerializerSettings()
                                                    Using reader As New StreamReader(memStream, Encoding.UTF8)
                                                        Using jsonR As New JsonTextReader(reader)
                                                            Dim serializer As JsonSerializer = JsonSerializer.Create(jsonSettings)
                                                            Me._Value = serializer.Deserialize(jsonR, objType)
                                                        End Using
                                                    End Using
                                            End Select
                                        Else
                                            Throw New ApplicationException("No TypeName available to deserialize Cached Xml Value " & Convert.ToBase64String(Me._SerializedValue))
                                        End If
                                    Case CacheDistributionType.NativeBinary
                                        Me._Value = ReflectionHelper.Instance.BinaryFormatter.Deserialize(memStream)
                                End Select
                            End Using
                        Catch ex As Exception
                            DotNetNuke.Services.Exceptions.Exceptions.LogException(ex)
                        End Try
                    Case CacheDistributionType.AsString
                        Dim stringValue As String = Encoding.UTF8.GetString(Me._SerializedValue)
                        Dim objType As Type = ReflectionHelper.CreateType(Me._SerializedTypeName)
                        Me._Value = TypeDescriptor.GetConverter(objType).ConvertFromInvariantString(stringValue)
                End Select
            End If
        End Sub


        Private Shared Function Compress(ByVal bytes As Byte()) As Byte()
            Using ms As MemoryStream = New MemoryStream
                Using gzs As DeflateStream = New DeflateStream(ms, CompressionMode.Compress, False)
                    gzs.Write(bytes, 0, bytes.Length)
                End Using
                ms.Close()
                Return ms.ToArray
            End Using
        End Function



        Private Shared Function Decompress(ByVal bytes As Byte()) As Byte()
            Dim toReturn As Byte()
            Using ms As MemoryStream = New MemoryStream(bytes, False)
                Using gzs As DeflateStream = New DeflateStream(ms, CompressionMode.Decompress, False)
                    Using dest As MemoryStream = New MemoryStream
                        Dim read As Integer
                        Dim tmp As Byte() = New Byte(bytes.Length - 1) {}
                        read = gzs.Read(tmp, 0, tmp.Length)
                        Do While (read <> 0)
                            dest.Write(tmp, 0, read)
                            read = gzs.Read(tmp, 0, tmp.Length)
                        Loop
                        dest.Close()
                        toReturn = dest.ToArray
                    End Using
                End Using
            End Using
            Return toReturn
        End Function

        'Dim filteredStream As Stream = memStream

        'If memStream.Length > Me._CompressionThresold Then
        '    Me._IsCompressed = True
        '    filteredStream = New DeflateStream(memStream, CompressionMode.Compress)
        'End If
        'Using br As New BinaryReader(filteredStream)
        '    Me._SerializedValue = br.ReadBytes(Convert.ToInt32(filteredStream.Length))
        'End Using

        'Dim buffer(16 * 1024) As Byte
        'Using ms As New MemoryStream
        '    Dim read As Integer = memStream.Read(buffer, 0, buffer.Length)
        '    Dim length As Integer
        '    While read > 0
        '        length += read
        '        ms.Write(buffer, 0, read)
        '        read = memStream.Read(buffer, 0, buffer.Length)
        '    End While
        '    If length > Me._CompressionThresold Then
        '        Dim filteredStream As New DeflateStream(memStream, CompressionMode.Compress)
        '        Me._IsCompressed = True
        '        Using br As New BinaryReader(filteredStream)
        '            Me._SerializedValue = br.ReadBytes(Convert.ToInt32(filteredStream.Length))
        '        End Using
        '    Else
        '        Me._SerializedValue = memStream.ToArray
        '    End If
        'End Using


#End Region

    End Class
End NameSpace